package com.evil.common.core.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.BooleanUtil;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.enums.SwitchEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.spring.SpringUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 缓存工具类-mapper
 */
@Slf4j
public class CacheMapperUtil {

    public static <T, V> Optional<T> cacheById(V id,
                                               Class<T> tClass,
                                               Function<V, T> select,
                                               Function<V, String> getKey,
                                               long time) {
        if (null == id) {
            return Optional.empty();
        }
        try {
            RedisUtil redisUtil = SpringUtils.getBean(RedisUtil.class);
            // 判断是否可以使用缓存
            Boolean cantUse = (Boolean) redisUtil.get(RedisKeyUtil.requestUuidClearCache());
            if (BooleanUtil.isTrue(cantUse)) {
                return Optional.ofNullable(select.apply(id));
            }
            String redisKey = getKey.apply(id);
            T t = tClass.cast(redisUtil.get(redisKey));
            if (Objects.isNull(t)) {
                return Optional.ofNullable(select.apply(id)).map(e -> {
                    redisUtil.set(redisKey, e, time);
                    return e;
                });
            }
            return Optional.of(t);
        } catch (Exception e) {
            log.error("[CacheMapper]", e);
            throw new BusinessException(RCodeEnum.ERROR_RUNTIME, (Object) e.getMessage());
        }
    }

    public static <T, V> List<T> cacheListById(V id,
                                               Class<T> tClass,
                                               Function<V, List<T>> select,
                                               Comparator<T> comparator,
                                               Function<V, String> getKey,
                                               long time) {
        if (null == id) {
            return new ArrayList<>();
        }
        try {
            RedisUtil redisUtil = SpringUtils.getBean(RedisUtil.class);
            // 判断是否可以使用缓存
            Boolean cantUse = (Boolean) redisUtil.get(RedisKeyUtil.requestUuidClearCache());
            if (BooleanUtil.isTrue(cantUse)) {
                return select.apply(id);
            }

            // 获取缓存
            String redisKey = getKey.apply(id);
            List<?> list = (List<?>) redisUtil.get(redisKey);
            List<T> tList;
            // 获取缓存缺失
            if (Objects.isNull(list)) {
                tList = select.apply(id);
                tList.sort(comparator);
                redisUtil.set(redisKey, tList, time);
            } else {
                tList = list.stream().map(tClass::cast).sorted(comparator).collect(Collectors.toList());
            }
            return tList;
        } catch (Exception e) {
            log.error("[CacheMapper]", e);
            throw new BusinessException(RCodeEnum.ERROR_RUNTIME, (Object) e.getMessage());
        }
    }

    public static <T, V> Optional<T> cacheByIdAndVersion(V id,
                                                         Integer version,
                                                         Class<T> tClass,
                                                         BiFunction<V, Integer, Optional<T>> select,
                                                         BiFunction<V, Integer, String> getKey,
                                                         long time) {
        if (null == id) {
            return Optional.empty();
        }
        try {
            RedisUtil redisUtil = SpringUtils.getBean(RedisUtil.class);
            // 判断是否可以使用缓存
            Boolean cantUse = (Boolean) redisUtil.get(RedisKeyUtil.requestUuidClearCache());
            if (BooleanUtil.isTrue(cantUse)) {
                return select.apply(id, version);
            }

            // 获取缓存
            String redisKey = getKey.apply(id, version);
            T t = tClass.cast(redisUtil.get(redisKey));
            if (Objects.isNull(t)) {
                return select.apply(id, version).map(e -> {
                    redisUtil.set(redisKey, e, time);
                    return e;
                });
            }
            return Optional.of(t);
        } catch (Exception e) {
            log.error("[CacheMapper]", e);
            throw new BusinessException(RCodeEnum.ERROR_RUNTIME, (Object) e.getMessage());
        }
    }

    public static <T, V> List<T> cacheByIds(Collection<V> ids,
                                            boolean filterDeleted,
                                            Class<T> tClass,
                                            BiFunction<Collection<V>, Boolean, List<T>> select,
                                            Function<T, V> getId,
                                            Function<T, Integer> getIsDeleted,
                                            Comparator<T> comparator,
                                            Function<Collection<V>, Map<V, String>> getKeyMap,
                                            long time) {
        if (CollectionUtil.isEmpty(ids)) {
            return new ArrayList<>();
        }
        // 防止重复
        ids = new HashSet<>(ids);
        RedisUtil redisUtil = SpringUtils.getBean(RedisUtil.class);
        // 判断是否可以使用缓存
        Boolean cantUse = (Boolean) redisUtil.get(RedisKeyUtil.requestUuidClearCache());
        if (BooleanUtil.isTrue(cantUse)) {
            return select.apply(ids, filterDeleted);
        }

        try {
            // 获取缓存
            Map<V, String> keyMap = getKeyMap.apply(ids);
            List<T> list = redisUtil.get(tClass, keyMap.values());

            // 已有缓存的
            Set<V> existIds = StreamUtil.transSetT(list, getId);
            // 获取缓存缺失
            List<V> unCacheIds = StreamUtil.filter(ids, id -> !existIds.contains(id));
            // 返回结果
            List<T> result = new ArrayList<>();

            // 缺失重新缓存
            if (CollectionUtil.isNotEmpty(unCacheIds)) {
                result.addAll(select.apply(unCacheIds, filterDeleted));
                result.forEach(o -> redisUtil.set(keyMap.get(getId.apply(o)), o, time));
            }

            // 是否过滤已删除
            Predicate<T> filter = filterDeleted ? o -> !SwitchEnum.isOpen(getIsDeleted.apply(o)) : o -> true;

            result.addAll(StreamUtil.filter(list, filter));
            // 排序
            if (null != comparator) {
                result.sort(comparator);
            }
            return result;
        } catch (Exception e) {
            log.error("[CacheMapper]", e);
            throw new BusinessException(RCodeEnum.ERROR_RUNTIME, (Object) e.getMessage());
        }
    }

    public static <T, V> List<T> cacheListByIds(Collection<V> ids,
                                                boolean filterDeleted,
                                                Class<T> tClass,
                                                BiFunction<Collection<V>, Boolean, List<T>> select,
                                                Function<T, V> getId,
                                                Function<T, Integer> getIsDeleted,
                                                Function<Collection<V>, Map<V, String>> getKeyMap,
                                                long time) {
        if (CollectionUtil.isEmpty(ids)) {
            return new ArrayList<>();
        }
        // 防止重复
        ids = new HashSet<>(ids);
        RedisUtil redisUtil = SpringUtils.getBean(RedisUtil.class);
        // 判断是否可以使用缓存
        Boolean cantUse = (Boolean) redisUtil.get(RedisKeyUtil.requestUuidClearCache());
        if (BooleanUtil.isTrue(cantUse)) {
            return select.apply(ids, filterDeleted);
        }

        try {
            // 获取缓存
            Map<V, String> keyMap = getKeyMap.apply(ids);
            List<?> lists = redisUtil.get(List.class, keyMap.values());

            List<T> list = new ArrayList<>();
            if (!CollectionUtil.isEmpty(lists)) {
                lists.forEach(l -> {
                    List<?> tList = (List<?>) l;
                    tList.forEach(t -> list.add(tClass.cast(t)));
                });
            }
            // 已有缓存的
            Set<V> existIds = StreamUtil.transSetT(list, getId);
            // 获取缓存缺失
            List<V> unCacheIds = StreamUtil.filter(ids, id -> !existIds.contains(id));
            // 返回结果
            List<T> result = new ArrayList<>();

            // 缺失重新缓存
            if (CollectionUtil.isNotEmpty(unCacheIds)) {
                result.addAll(select.apply(unCacheIds, filterDeleted));
                StreamUtil.groupK(result, getId).forEach((v, tList) -> redisUtil.set(keyMap.get(v), tList, time));
            }

            // 是否过滤已删除
            Predicate<T> filter = filterDeleted ? o -> !SwitchEnum.isOpen(getIsDeleted.apply(o)) : o -> true;

            result.addAll(StreamUtil.filter(list, filter));
            return result;
        } catch (Exception e) {
            log.error("[CacheMapper]", e);
            throw new BusinessException(RCodeEnum.ERROR_RUNTIME, (Object) e.getMessage());
        }
    }
}
